package com.atguigu.gulimall.order.service.impl;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.TypeReference;
import com.atguigu.common.exception.SkuNoStockException;
import com.atguigu.common.to.OrderTo;
import com.atguigu.common.utils.R;
import com.atguigu.common.vo.MemberOAuthVo;
import com.atguigu.gulimall.order.constant.OrderConstant;
import com.atguigu.gulimall.order.constant.OrderEnum;
import com.atguigu.gulimall.order.dao.OrderDao;
import com.atguigu.gulimall.order.entity.OrderItemEntity;
import com.atguigu.gulimall.order.feign.CartFeignService;
import com.atguigu.gulimall.order.feign.MemberFeignService;
import com.atguigu.gulimall.order.feign.ProductFeignService;
import com.atguigu.gulimall.order.feign.WareFeignService;
import com.atguigu.gulimall.order.intercepter.OrderLoginIntercepter;
import com.atguigu.gulimall.order.service.OrderItemService;
import com.atguigu.gulimall.order.to.OrderCreateTo;
import com.atguigu.gulimall.order.vo.*;

import com.baomidou.mybatisplus.core.toolkit.IdWorker;
import org.springframework.amqp.rabbit.core.RabbitTemplate;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.data.redis.core.script.DefaultRedisScript;
import org.springframework.stereotype.Service;

import java.math.BigDecimal;
import java.util.*;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.function.Function;
import java.util.stream.Collectors;

import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.atguigu.common.utils.PageUtils;
import com.atguigu.common.utils.Query;

import com.atguigu.gulimall.order.entity.OrderEntity;
import com.atguigu.gulimall.order.service.OrderService;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.web.context.request.RequestAttributes;
import org.springframework.web.context.request.RequestContextHolder;

//@RabbitListener(queues = {"durunwu.queue"})
@Service("orderService")
public class OrderServiceImpl extends ServiceImpl<OrderDao, OrderEntity> implements OrderService {

    @Autowired
    RabbitTemplate rabbitTemplate;

    @Autowired
    OrderItemService orderItemService;

    @Autowired
    ProductFeignService productFeignService;

    @Autowired
    StringRedisTemplate redisTemplate;

    @Autowired
    WareFeignService wareFeignService;

    @Autowired
    ThreadPoolExecutor executor;

    @Autowired
    CartFeignService cartFeignService;

    @Autowired
    MemberFeignService memberFeignService;

    @Override
    public PageUtils queryPage(Map<String, Object> params) {
        IPage<OrderEntity> page = this.page(
                new Query<OrderEntity>().getPage(params),
                new QueryWrapper<OrderEntity>()
        );

        return new PageUtils(page);
    }

//    @
//    public void msgListener1(OrderEntity orderEntity){
//        System.out.println("接受消息orderEntity：" + orderEntity);
//    }
//
//    @RabbitHandler
//    public void msgListener2(Message message, OrderItemEntity orderItemEntity, Channel channel) throws IOException {
//        //顺序id,channel内按顺序自增了
//        long deliveryTag = message.getMessageProperties().getDeliveryTag();
//        if (deliveryTag % 2 == 0){
//            //basicAck(long deliveryTag, boolean multiple)
//            channel.basicAck(deliveryTag,false);
//            System.out.println("自增id："+deliveryTag+"，签收完成" + orderItemEntity.getId());
//        } else {
//            //basicNack(long deliveryTag, boolean multiple, boolean requeue)
//            channel.basicNack(deliveryTag,false,true);
//            System.out.println("自增id："+deliveryTag+"，拒签"+orderItemEntity.getId());
//        }
//
//    }

    /**
     * 展示结算页信息
     *
     * Feign 丢失请求头的问题？ 如果使用ThreadLocal的方式直接获取id会获取不到,远程调用没经过拦截器获取不到id
     * 异步丢失
     */
    @Override
    public OrderConfirmVo confirmOrder() throws ExecutionException, InterruptedException {
        OrderConfirmVo orderConfirmVo = new OrderConfirmVo();
        MemberOAuthVo memberOAuthVo = OrderLoginIntercepter.threadLocal.get();
        //TODO :获取当前线程请求头信息(解决Feign异步调用丢失请求头问题)
        RequestAttributes requestAttributes = RequestContextHolder.getRequestAttributes();


        CompletableFuture<Void> memberRunAsync = CompletableFuture.runAsync(new Runnable() {
            @Override
            public void run() {
                //每一个线程都来共享之前的请求数据
                RequestContextHolder.setRequestAttributes(requestAttributes);

                //1.查询用户信息
                List<MemberAddressVo> list = memberFeignService.getMemberReceiveAddress(memberOAuthVo.getId());
                orderConfirmVo.setMemberAddressVos(list);
            }
        }, executor);

        CompletableFuture<Void> orderRunAsync = CompletableFuture.runAsync(new Runnable() {
            @Override
            public void run() {
                //每一个线程都来共享之前的请求数据
                RequestContextHolder.setRequestAttributes(requestAttributes);

                //2.查询商品信息
                List<OrderItemVo> orderItemVos = cartFeignService.orderFeignCart(memberOAuthVo.getId());
                orderConfirmVo.setItems(orderItemVos);
            }
        }, executor).thenRunAsync(new Runnable() {
            @Override
            public void run() {
                //查库存  怎么查库存  查库存参数List<Long> skuIds
                List<Long> skuIds = orderConfirmVo.getItems().stream().map(item -> {
                    return item.getSkuId();
                }).collect(Collectors.toList());
                R<List<SkuHasStockVo>> r = wareFeignService.getSkuHasStock(skuIds);
                if (r.getCode() == 0){
                    List<SkuHasStockVo> data = r.getData(new TypeReference<List<SkuHasStockVo>>() {
                    });
                    // 下界 接收参数的类型，上界 返回的类型
                    //Function<? super T, ? extends K> keyMapper,
                    //Function<? super T, ? extends U> valueMapper
                    Map<Long, Boolean> collect = data.stream().collect(Collectors.toMap(new Function<SkuHasStockVo, Long>() {
                        @Override
                        public Long apply(SkuHasStockVo skuHasStockVo) {
                            return skuHasStockVo.getSkuId();
                        }
                    }, SkuHasStockVo::getHasStock));
                    orderConfirmVo.setStocks(collect);
                }

            }
        });

        //会员积分
        orderConfirmVo.setIntegration(memberOAuthVo.getIntegration());

        //设置订单token,防止页面重复提交，保证幂等性
        String key = OrderConstant.ORDER_TOKEN_PREFIX + memberOAuthVo.getId();
        String orderToken = UUID.randomUUID().toString().replace("_","");
        redisTemplate.opsForValue().set(key,orderToken) ;

        //set token
        orderConfirmVo.setOrderToken(orderToken);

        CompletableFuture.allOf(memberRunAsync,orderRunAsync).get();
        return orderConfirmVo;
    }


    /**
     * 下单
     * 1.校验订单令牌token,校验成功进行下单操作，校验失败返回code
     *
     * 2.创建订单
     *     （1）订单号    1
     *     （2）用户信息：地址  1
     *     （3）购物车商品信息：应该直接查询购物车数据，不能获取结算页的商品数据，  list
     *     （4）下单金额（验价） 根据购物车商品信息进行验价  1
     *
     * 3.下单成功，锁库存，跳转到支付页
     *   下单失败，调回到订单页 -> 重新确认订单信息
     *      下单失败原因：重复提交页面，下单金额对比不一致，商品库存不足
     */
//    @GlobalTransactional//全局事务
    @Transactional
    @Override
    public SubmitOrderResponseVo placeOrder(OrderSubmitVo submitVo) {
        SubmitOrderResponseVo submitOrderResponseVo = new SubmitOrderResponseVo();
        submitOrderResponseVo.setCode(0);
        MemberOAuthVo memberOAuthVo = OrderLoginIntercepter.threadLocal.get();
        String key = OrderConstant.ORDER_TOKEN_PREFIX + memberOAuthVo.getId();
        //原子性操作：1.取出token 2.对比token 3.删除token
        //execute(RedisScript<T> script, List<K> keys, Object... args)
        String script = "if redis.call('get', KEYS[1]) == ARGV[1] then return redis.call('del', KEYS[1]) else return 0 end";
        Long luaReturn = redisTemplate.execute(new DefaultRedisScript<Long>(script, Long.class), Arrays.asList(key), submitVo.getOrderToken());
        //1-删除成功   0-删除失败
        if (luaReturn.equals(1L)){
            //令牌删除成功 -> 创建订单
            OrderCreateTo order = createOrder(submitVo);

            //验价 应付价格-
            BigDecimal payAmount = order.getOrder().getPayAmount();//应付价格
            BigDecimal payPrice = submitVo.getPayPrice();
            double abs = Math.abs(payAmount.subtract(payPrice).doubleValue());
            if (abs < 0.01){
                //验价成功 -> 保存订单信息 -> 锁定库存

                //TODO 保存订单信息
                saveOrder(order.getOrder(),order.getOrderItems());

                //远程锁定库存
                List<OrderItemEntity> orderItems = order.getOrderItems();
                List<OrderItemVo> collect = orderItems.stream().map(item -> {
                    OrderItemVo orderItemVo = new OrderItemVo();
                    orderItemVo.setSkuId(item.getSkuId());
                    orderItemVo.setCount(item.getSkuQuantity());
                    return orderItemVo;
                }).collect(Collectors.toList());
                OrderStockRequest orderStockRequest = new OrderStockRequest();
                orderStockRequest.setOrderSn(order.getOrder().getOrderSn());
                orderStockRequest.setItemVos(collect);

                //TODO 远程锁库存
                R r = wareFeignService.orderStock(orderStockRequest);
                if (r.getCode() == 0){

                    //TODO 订单创建成功，开始计时
                    //convertAndSend(String exchange, String routingKey, Object object, @Nullable CorrelationData correlationData)
                    rabbitTemplate.convertAndSend("order-event-exchange","order.create.order",order.getOrder());

                    submitOrderResponseVo.setCode(0);
                    submitOrderResponseVo.setOrder(order.getOrder());
                    return submitOrderResponseVo;
                }else {
                    //锁库存失败
                    //锁定失败
                    String msg = (String) r.get("msg");
                    throw new SkuNoStockException(msg);
                }

            }else {
                //验价失败
                submitOrderResponseVo.setCode(2);
                return submitOrderResponseVo;
            }

        }else {
            //令牌删除失败
            submitOrderResponseVo.setCode(1);
            return submitOrderResponseVo;
        }

    }

    /**
     * 延时队列关闭订单
     */
    @Override
    public void orderClose(OrderEntity order) {
        //如果status=0，就改成4已关闭
        if (Objects.equals(order.getStatus(), OrderEnum.CREATE_NEW.getCode())){
            OrderEntity orderEntity = new OrderEntity();
            orderEntity.setId(order.getId());
            orderEntity.setStatus(OrderEnum.CANCLED.getCode());
            //TODO 如果订单异常回滚了订单数据，这里会找不到订单，修改失败
            this.updateById(orderEntity);

            //给mq发消息，直接解锁库存，不延时
            OrderEntity entity = getById(order.getId());
            OrderTo orderTo = new OrderTo();
            BeanUtils.copyProperties(entity,orderTo);
            //convertAndSend(String exchange, String routingKey, Object object, @Nullable CorrelationData correlationData)
            rabbitTemplate.convertAndSend("order-event-exchange","order.release.other",orderTo);

        }
    }

    /**
     * 保存订单信息
     */
    private void saveOrder(OrderEntity order, List<OrderItemEntity> orderItems) {
        this.save(order);
        orderItemService.saveBatch(orderItems);
    }

    /**
     *  创建订单
     *     （1）用户信息：地址 1
     *     （2）购物车商品信息：应该直接查询购物车数据，不能获取结算页的商品数据，  list
     *     （3）下单金额  1
     *      (4) 验价
     */
    public OrderCreateTo createOrder(OrderSubmitVo submitVo){
        OrderCreateTo orderCreateTo = new OrderCreateTo();
        //订单号-唯一id
        String timeId = IdWorker.getTimeId();
        OrderEntity orderEntity = buildOrderEntity(submitVo, timeId);
        List<OrderItemEntity> orderItemEntityList = buildOrderItems(timeId);
        //封装：订单总额、应付总额
        OrderEntity order = computePrice(orderEntity,orderItemEntityList);
        orderCreateTo.setOrder(order);
        orderCreateTo.setOrderItems(orderItemEntityList);
        return orderCreateTo;
    }

    /**
     * 封装OrderEntity：
     * 订单总额、应付总额
     * 促销优化金额、积分抵扣金额、优惠券抵扣金额
     *
     */
    public OrderEntity computePrice(OrderEntity order,List<OrderItemEntity> orderItems){
        //订单总额
        BigDecimal totalAmount = new BigDecimal("0.0");
        //促销优化金额、积分抵扣金额、优惠券抵扣金额
        BigDecimal promotionAmount = new BigDecimal("0.0");
        BigDecimal integrationAmount = new BigDecimal("0.0");
        BigDecimal couponAmount = new BigDecimal("0.0");
        //积分、成长值
        Integer integration = 0;
        Integer growth = 0;
        for (OrderItemEntity orderItem : orderItems) {
            totalAmount = totalAmount.add(orderItem.getRealAmount());
            promotionAmount = promotionAmount.add(orderItem.getPromotionAmount());
            integrationAmount = integrationAmount.add(orderItem.getIntegrationAmount());
            couponAmount = couponAmount.add(orderItem.getCouponAmount());
            integration += orderItem.getGiftIntegration();
            growth += orderItem.getGiftGrowth();
        }

        //订单总额
        order.setTotalAmount(totalAmount);
        //应付总额 = 订单总额 + 运费
        order.setPayAmount(totalAmount.add(order.getFreightAmount()));
        //促销优化金额、积分抵扣金额、优惠券抵扣金额
        order.setPromotionAmount(promotionAmount);
        order.setIntegrationAmount(integrationAmount);
        order.setCouponAmount(couponAmount);
        //积分、成长值
        order.setIntegration(integration);
        order.setGrowth(growth);

        return order;
    }

    /**
     * 订单信息
     * OrderEntity
     *
     * 1.订单号
     * 2.用户地址信息
     * 3.运费金额
     */
    public OrderEntity buildOrderEntity(OrderSubmitVo submitVo,String timeId){
        OrderEntity orderEntity = new OrderEntity();

        orderEntity.setOrderSn(timeId);
        orderEntity.setCreateTime(new Date());

        //用户地址
        MemberOAuthVo memberOAuthVo = OrderLoginIntercepter.threadLocal.get();
        orderEntity.setMemberId(memberOAuthVo.getId());
        MemberAddressVo member = memberFeignService.getMember(memberOAuthVo.getId());
        orderEntity.setReceiverName(member.getName());
        orderEntity.setReceiverPhone(member.getPhone());
        orderEntity.setReceiverPostCode(member.getPostCode());
        orderEntity.setReceiverProvince(member.getProvince());
        orderEntity.setReceiverCity(member.getCity());
        orderEntity.setReceiverRegion(member.getRegion());
        orderEntity.setReceiverDetailAddress(member.getDetailAddress());

        //用户名
        orderEntity.setMemberUsername(memberOAuthVo.getUsername());

        //运费金额
        R r = wareFeignService.fare(submitVo.getAddrId());
        if (r.getCode() == 0){
            FareVo data = (FareVo)r.getData(new TypeReference<FareVo>() {
            });
            //运费金额
            orderEntity.setFreightAmount(data.getFare());
        }
        orderEntity.setPayType(submitVo.getPayType());

        //订单状态-0待付款
        orderEntity.setStatus(OrderConstant.ORDER_STATUS);

        //字段确认天数
        orderEntity.setAutoConfirmDay(7);

        return orderEntity;
    }

    /**
     * 订单商品
     * List<OrderItemEntity>  因为商品有多个，所以是list
     *
     * 1.订单号
     * 2.sku信息
     * 3.spu信息
     */
    public List<OrderItemEntity> buildOrderItems(String timeId){
        //查询redis商品信息
        MemberOAuthVo memberOAuthVo = OrderLoginIntercepter.threadLocal.get();
        List<OrderItemVo> orderItemVos = cartFeignService.orderFeignCart(memberOAuthVo.getId());
        List<OrderItemEntity> collect = orderItemVos.stream().map(item -> {
            OrderItemEntity orderItemEntity = new OrderItemEntity();
            //订单号
            orderItemEntity.setOrderSn(timeId);
            //spu信息
            SpuInfoEntity spuInfo = productFeignService.getSpuBySkuId(item.getSkuId());
            orderItemEntity.setSpuId(spuInfo.getId());
            orderItemEntity.setSpuName(spuInfo.getSpuName());
            orderItemEntity.setCategoryId(spuInfo.getCatalogId());
            //Sku信息
            orderItemEntity.setSkuId(item.getSkuId());
            orderItemEntity.setSkuName(item.getTitle());
            orderItemEntity.setSkuPic(item.getImage());
            orderItemEntity.setSkuPrice(item.getPrice());
            orderItemEntity.setSkuQuantity(item.getCount());
            //商品销售属性组合 List<String> -> String
            //集合 根据指定的分割符转换 为字符串
            String jsonString = JSON.toJSONString(item.getSkuAttrValues());
            orderItemEntity.setSkuAttrsVals(jsonString);
            //促销、优惠券、积分
            orderItemEntity.setPromotionAmount(BigDecimal.ZERO);
            orderItemEntity.setCouponAmount(BigDecimal.ZERO);
            orderItemEntity.setIntegrationAmount(BigDecimal.ZERO);
            //该商品经过优惠后的分解金额
            BigDecimal totalPrice = orderItemEntity.getSkuPrice().multiply(new BigDecimal(orderItemEntity.getSkuQuantity().toString()));
            BigDecimal realPrice = totalPrice.subtract(orderItemEntity.getPromotionAmount())
                            .subtract(orderItemEntity.getCouponAmount())
                                    .subtract(orderItemEntity.getIntegrationAmount());
            orderItemEntity.setRealAmount(realPrice);
            //赠送积分、成长值
            orderItemEntity.setGiftIntegration(totalPrice.intValue());
            orderItemEntity.setGiftGrowth(totalPrice.intValue());

            return orderItemEntity;
        }).collect(Collectors.toList());
        return collect;
    }


}